Технология Клиент-Сервер 2006'3 |
|||||||
|
Если упор делается на производительность Web-приложения, а доступ ограничен внутренними клиентами, может очень пригодиться HTTP-хендлер ASP.NET. HTTP-хендлеры ASP.NET не имеют равных по производительности, когда речь идет о доставке клиенту данных через Web.
Кроме того, что он предоставляет высокопроизводительный доступ к ресурсам сервера, хендлер ASP.NET дает дополнительную гибкость. Вы можете вызвать хендлеры из HTML-страниц, серверного или клиентского кода. Вы можете вызывать хендлеры даже из клиента Windows Forms. Что самое приятное – их легко писать.
Хендлер ASP.NET превосходит по производительности все альтернативы в каждом из описанных сценариев (см. Таблицу 1).
Тип | Выполненных запросов (сек.) |
Производительность по сравнению с хендлером |
---|---|---|
Хендлер | 243 | 0 % |
Страница с кодом | 126 | 51 % |
Web-сервис | 36 | 15 % |
Эффективность хендлера во многом обусловлена тем, чего в нем нет. Подумайте об ASP.NET-хендлере как об ASPX-файле, который не генерирует никаких событий, и не содержит HTML. Благодаря отсутствию этих элементов, порождаемая хендлером нагрузка ниже, а производительность – выше, чем у ASP.NET Web Form.
Первый шаг в создании хендлера – создать на ASP.NET -сайте новый файл с расширением ASHX (см. рисунок 1). Этот файл должен содержать директиву обработки WebHandler и, если вы собираетесь поместить код непосредственно в ASHX-файл, атрибуты Language и Class. Этот пример содержит хендлер, написанный на Visual Basic, и определяющий класс с названием EmployeeInfo:
<%@ WebHandler Language="VB" Class="EmployeeInfo" %> |
ASP.NET по умолчанию не создает никакого кода для ASHX-файлов. Многие программисты предпочитают размещать свой код в отдельном файле, особенно в Visual Studio 2003, где для ASHX-файлов нет IntelliSense, подсветки синтаксиса и поддержки отладки. Чтобы использовать файл с кодом из ASHX-файла, нужно добавить в директиву WebHandler атрибут CodeBehind, и задать имя внешнего файла в качестве значения этого атрибута. Затем на Web-сайте размещается Class-файл с указанным именем (Visual Studio 2005 помещает этот файл в папку App_Code вашего проекта). Теперь код больше на размещается в ASHX-файле, и атрибут Language можно удалить из директивы WebHandler:
<%@ WebHandler Class="EmployeeInfo" CodeBehind="EmployeeInfo.vb" %> |
Код хендлера должен реализовать интерфейс IHttpHandler. При реализации этого интерфейса нужно написать код одного метода (ProcessRequest) и одного свойства (IsReusable).
Масштабируемость приложения можно увеличить, заставив свойство IsReusable возвращать значение True. Это заставит ASP.NET кэшировать хендлер после первого же пользовательского обращения к нему. Если хендлер задействует какой-либо дефицитный ресурс или используется редко, это свойство должно возвращать False.
Можно также указать тип контента, возвращаемого хендлером, используя свойство ContentType объекта Context, который ASP.NET передает методу ProcessRequest. Протестировать хендлер можно просто сделав ASHX-файл стартовой страницей и нажав F5 (если значение свойства ContentType - "text/HTML"). Стартовая точка хендлера после этих изменений будет выглядеть примерно так:
Imports System Imports System.Web Public Class EmployeeInfo Implements IHttpHandler Public Sub ProcessRequest(ByVal context As HttpContext) _ Implements IHttpHandler.ProcessRequest Context.Response.ContentType = "text/HTML" End Sub Public ReadOnly Property IsReusable() As Boolean _ Implements IHttpHandler.IsReusable Get Return True End Get End Property End Class |
К этому моменту вы создали скелет хендлера и поместили бизнес-логику в метод ProcessRequest вашего хендлера (при обращении клиента к хендлеру ASP.NET вызывает этот метод автоматически). У вас также есть доступ к ряду существующих ASP.NET-объектов (включая объекты Request, Response и User) через объект Context, переданный методу ProcessRequest.
Простейший из возможных ProcessRequest возвращает строку, используя метод Write объекта Response:
Public Sub ProcessRequest(ByVal context _ As HttpContext) Implements _ IHttpHandler.ProcessRequest context.Response.ContentType = _ "text/HTML" context.Response.Write("Hello World") End Sub |
Объект Request можно использовать также для доступа к любой информации, отправляемой клиентом на сервер в качестве части запроса. Следующий код в процедуре ProcessRequest берет значение параметра "name" из строки запроса и встраивает его в строку, отправляемую обратно браузеру:
context.Response.Write("Hello, " & context.Request.QueryString("name")) |
Вы можете запросить хендлер, введя его URL в адресную строку любого браузера. Типичное обращение к хендлеру может выглядеть так:
http://MyServer/MyHandlerSite/EmployeeInfo.ashx?name=Peter |
Можно обратиться к хендлеру, используя только URL, что позволяет сделать это с любой Web-страницы. То, что доступ к HTTP-хендлерам описывается в виде URL, позволяет ссылаться на них из HTML-атрибутов, предусматривающих ссылку на URL. Так, на HTTP-хендлер можно ссылаться в атрибуте src тега img или iframe:
<form id="form1" runat="server"> <div> <iframe src="EmployeeInfo.ashx?name=Peter" /> </div> </form> |
Использование методов объекта Response для передачи содержимого файлов и потоков клиенту позволяет создать хендлер, дающий клиенту возможность запросить файл у сервера. Например, можно использовать метод WriteFile для передачи содержимого файла напрямую в поток вывода без буферизации. Следующий код использует значение параметра «name», получаемого с помощью функции QueryString, для задания локального пути на сервере к графическому файлу, отправляемому клиенту:
context.Response.WriteFile(context.Request.QueryString("name") & ".jpg") |
Этот хендлер можно вызвать из атрибута src тега image. Следующий файл добавляет на страницу изображение, хранящееся в файле:
<img src="Photo.ashx?EmployeeName=davolio" alt="Some employee's picture" /> |
Хендлер можно интегрировать в окружающее его приложение, обращаясь к пользовательским данным в объекте Session из кода хендлера. К сожалению, по умолчанию вы получите сообщение "Object reference not set to an instance of an object" при попытке использовать свойство Session объекта Context. Реализация интерфейса IRequiresSessionState позволяет получить доступ к объекту Session. Этот интерфейс не требует реализации каких-либо методов или свойств – его наличие сигнализирует ASP.NET о необходимости создания экземпляра объекта Session.
Public Class EmployeeInfo Implements IHttpHandler Implements IRequiresSessionState |
Этот, более сложный, пример считывает данные из БД Northwind для создания HTML-таблицы с информацией о сотрудниках. Заметьте, что вы не вытягиваете информацию из querystring. Вместо этого для определения того, информация о каких сотрудниках должна быть выведена, используется значение из объекта Session. Для отображения фотографии сотрудника, на которое указывает поле PhotoPath, код строит тег img (см. листинг 1 и рисунок 2).
Dim sbOutPut As New StringBuilder Dim con As New SqlConnection( _ ConfigurationManager.ConnectionStrings( _ “NwindConnection”).ConnectionString) Dim cmd As SqlCommand = con.createcommand cmd.CommandText = _ “Select EmployeeId, LastName, FirstName, “ & _ “ BirthDate, PhotoPath from Employees “ & _ “ Where LastName ='” & _ context.Session(“EmployeeName”) & “';” con.Open() Dim rdr As SqlDataReader = cmd.ExecuteReader sbOutput.Append(“<table>”) If rdr.Read() Then For ing As Integer = 0 To rdr.FieldCount - 1 sbOutPut.Append(“<tr>”) sbOutPut.Append(“<td>” & rdr.GetName(ing) _ & “</td>”) Select Case rdr.GetName(ing) Case “PhotoPath” sbOutPut.Append( _ “<td><img src=” & rdr(ing) & “/></td>”) Case Else sbOutPut.Append(“<td>” & rdr(ing) & “</td>”) End Select sbOutPut.Append(“</tr>”) Next End If con.Close sbOutPut.Append(“</table>”) context.Response.Write(sbOutPut.ToString) |
Приведенные до сих пор примеры работают с текстом на базовом уровне. Однако хендлер можно использовать и для передачи клиенту бинарных данных, если считать, что клиент сможет их обработать. В таблице Employee БД Northwind в колонке Photo лежит BLOB с фотографией сотрудника. К счастью, тег <img> в броузере рассчитан на получение строки бинарных данных. Все, что должен сделать хендлер – извлечь из поля бинарные данные и отправить их клиенту.
Бинарное поле читается с помощью метода GetBytes объекта DataReader. Этот метод придется вызвать два раза: сперва с Nothing в качестве третьего параметра для определения числа байт в поле. Затем, используя это значение, задается размер буфера. После этого опять вызывается GetBytes, с буфером в качестве третьего параметра, для загрузки данных из поля в буфер.
Начните с выборки записи о сотруднике в объект Session:
Dim Photo() As Byte Dim lngLength As Long Dim sbOutPut As New StringBuilder Dim con As New SqlConnection( _ ConfigurationManager.ConnectionStrings( _ "NwindConnection").ConnectionString) Dim cmd As SqlCommand = con.createcommand cmd.CommandText = _ "Select Photo " & _ " Where LastName ='" & _ context.Session("EmployeeName") & "';" |
Теперь извлеките данные поля Photo:
con.Open() Dim rdr As SqlDataReader = cmd.ExecuteReader If rdr.Read() Then lngLength = rdr.GetBytes(0, 0, Nothing, 0, _ Integer.MaxValue) - 1 ReDim Photo(lngLength) rdr.GetBytes(0, 0, Photo, 0, lngLength) con.Close |
OutputStream объекта Response предоставляет механизм поточной передачи данных клиенту. Метод Write OutputStream-а используется для передачи клиенту данных из буфера. Заметьте, что первые 78 байт поля Photo нужно пропустить, чтобы получить в теге <img> корректный результат... по причинам, мне неизвестным (в этих полях хранятся OLE-объекты, можно предположить, что эти 78 байт – ни что иное, как заголовок OLE-объекта – прим.ред.):
context.Response.OutputStream.Write(Photo, 78, lngLength - 78) |
Используйте этот тег для вызова хендлера и отображения содержимого поля Photo на Web-странице:
<img src="EmployeePhoto.ashx" alt="Empoyee photo" /> |
Хендлеры можно вызывать не только из HTML-тегов. Если нужно обработать в клиентском коде результаты, возвращенные хендлером, можно использовать для его вызова объект XMLHTTP. Следующий код (для IE) использует объект XMLHTTP для вызова хендлера и получения результата:
var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); xmlhttp.open("GET", "http://MyServer/MyHandlerSite/EmployeeInfo.ashx?name=Davolio", false); xmlhttp.Send(null); var result = xmlhttp.responseText; |
А эта версия кода работает в последних версиях FireFox, Mozilla, Safari и Netscape:
var xmlhttp = new XMLHttpRequest(); xmlhttp.open("GET", "http://MyServer/MyHandlerSite/EmployeeInfo.ashx?name=Davolio", false); xmlhttp.send(null); var result = xmlhttp.responseText; |
Хендлер можно вызвать также из серверного кода WebForm, используя объект WebClient. Метод DownloadString объекта WebClient принимает URL хендлера и возвращает результат в виде строки:
Dim wc As New System.Net.WebClient Me.TextBox1.Text = wc.DownloadString( _ "http://MyServer/MyHandlerSite/" & _ "EmployeeInfo.ashx?name=Peter") |
Заметьте, что вам не стоит вызывать хендлер со страницы того же сайта, поскольку это не эффективно. Вместо этого лучше создать объект с необходимой функциональностью и добавить хендлер, выступающий в качестве «фасада» этого объекта. Такой дизайн позволит обращаться к объекту напрямую из серверного кода WebForms, расположенного на том же сайте.
Объект WebClient также полезен. Основанный на WebClient код можно использовать с других сайтов или в клиенте Windows Form для получения данных от ASP.NET-хендлера. Объекты WebClient и XMLHTTP предоставляют любому клиенту высокоэффективный способ получения данных от Web-приложения. Клиентам не нужно создавать прокси с хендлером, в отличие от использования клиентов с Web-сервисами. Клиенты также предоставляют больше способов для интеграции вызовов в код.
Конечно, даром ничего не дается. Если вы отходите от Web-сервисов, вы также отходите и от стандартов, предоставляемых спецификацией Web-сервисов. Вы больше не сможете положиться на WSDL-файл для определения формата сообщений, и вынуждены будете создавать и обрабатывать собственные форматы. Хендлеры эффективны, но их нужно использовать для передачи данных, только если заодно создаются и клиенты, работающие с этими хендлерами.
Теперь вы знаете все, чтобы реализовать синхронный ASP.NET-хендлер. Но вы можете создавать и асинхронные хендлеры, дающие лучшую масштабируемость, чем рассматривавшиеся здесь синхронные. Одно предупреждение: асинхронные хендлеры сложнее писать, чем синхронные. Чтобы воспользоваться асинхронной обработкой, придется запустить новый поток и создать новый класс, возвращающий объект IAsyncResult.
v
v
Copyright © 1994-2016 ООО "К-Пресс"